﻿using System;
using System.Text;
using System.Reflection;
using EaseDapper.Interface;
using EaseDapper.Utilities;
using System.Linq.Expressions;
using System.Collections.Concurrent;

namespace EaseDapper
{
    public class SetBuilderExpression : IBuilder
    {
        private readonly ConcurrentQueue<object> SetQueue = new ConcurrentQueue<object>();

        public void Resolver(Expression expression, bool isLeft)
        {
            switch (expression.NodeType)
            {
                case ExpressionType.Constant:
                    VisitConstantExpression(expression, isLeft);
                    break;
                case ExpressionType.MemberAccess:
                    VisitMemberExpression(expression, isLeft);
                    break;
                case ExpressionType.Call:
                    VisitCallExpression(expression, isLeft);
                    break;
                case ExpressionType.Convert:
                    VisitUnaryExpression(expression, isLeft);
                    break;
                default:
                    VisitBinaryExpression(expression, isLeft);
                    break;
            }
        }

        public void VisitBinaryExpression(Expression expression, bool isLeft)
        {
            BinaryExpression e = (BinaryExpression)expression;
            Resolver(e.Left, true);
            SetQueue.Enqueue(",");
            if (e.Right is MemberExpression rightMember && rightMember.Member.MemberType.Equals(MemberTypes.Property))
            {
                VisitMemberExpression(rightMember, false);
            }
            else
            {
                Resolver(e.Right, false);
            }
        }
        public void VisitCallExpression(Expression expression, bool isLeft)
        {
            var ex = (MethodCallExpression)expression;
            if (ex.Object is null)
            {
                Resolver(ex.Arguments[0], true);
                SetQueue.Enqueue(ex.Method.Name.TransferExpressionCustomMethod());
                if (ex.Arguments[1] is MemberExpression me)
                {
                    object value = GetDataExtensions.GetMemberValue(me);
                    SetQueue.Enqueue($"'{value}'");
                }
                else
                {
                    Resolver(ex.Arguments[1], false);
                }
            }
            else
            {
                Resolver(ex.Object, true);
                SetQueue.Enqueue(ex.Method.Name.TransferExpressionCustomMethod());
                if (ex.Arguments[0] is MemberExpression me)
                {
                    object value = GetDataExtensions.GetMemberValue(me);
                    SetQueue.Enqueue($"'{value}'");
                }
                else
                {
                    Resolver(ex.Arguments[0], false);
                }
            }
        }

        public void VisitUnaryExpression(Expression expression, bool isLeft)
        {
            var e = (UnaryExpression)expression;
            Resolver(e.Operand, isLeft);
        }

        public void VisitConstantExpression(Expression expression, bool isLeft)
        {
            var e = (ConstantExpression)expression;
            object value;
            if (e.Type.IsString())
            {
                value = e.Value;
            }
            else if (e.Type.IsBool())
            {
                value = Convert.ToInt32(e.Value);
            }
            else if (e.Type.IsEnum())
            {
                value = Convert.ToInt32(e.Value);
            }
            else if (e.Type.IsDateTime())
            {
                value = e.Value;
            }
            else
            {
                value = e.Value;
            }
            SetQueue.Enqueue(isLeft ? $"`{value}`" : $"'{value}'");
        }

        public void VisitMemberExpression(Expression expression, bool isLeft)
        {
            if (expression is MemberExpression me)
            {
                object value = GetDataExtensions.GetMemberValue(me);
                //if (string.IsNullOrWhiteSpace(value.ToString()))
                //{
                //    value = "\'\'";
                //}
                SetQueue.Enqueue(isLeft ? $"`{value ?? me.Member.Name}`" : $"'{value ?? me.Member.Name}'");
            }
        }

        public string BuilderData()
        {
            StringBuilder sqlWhere = new StringBuilder();
            while (!SetQueue.IsEmpty)
            {
                if (SetQueue.TryDequeue(out var value))
                {
                    sqlWhere.Append(value);
                }
            }
            return sqlWhere.ToString();
        }
    }
}
